/* 
 * File:   SocketHandler.cpp
 * Author: Bram
 * 
 * Created on January 9, 2013, 1:36 PM
 */

#include "GeneralSocket.h"


GeneralSocket::GeneralSocket(int portNum) {
    portNumber = portNum;
    blocking = 1;
    
    
    
    if ( (socketId=socket(AF_INET,SOCK_STREAM,0)) == -1)
    {
        dasLog << "Error 005 creating socket!" << endl;
        cout << "Error 005 creating socket!" << endl;
        close(socketId);
        exit(1);
    }
    
    bzero((char *) &serverAddr, sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serverAddr.sin_port = htons(portNum);
    
    setKeepAlive(1);
}

GeneralSocket::GeneralSocket(const GeneralSocket& orig) {
}

GeneralSocket::~GeneralSocket() {
}
    
void GeneralSocket::setDebug(int debugToggle)
{

    if ( setsockopt(socketId,SOL_SOCKET,SO_DEBUG,(char *)&debugToggle,sizeof(debugToggle)) < 0 )
    {
        dasLog << "Error 006 getting host by name" << endl;
        cout  << "Error 006 getting host by name" << endl;
        close(socketId);
        exit(1);
    }
}

void GeneralSocket::setReuseAddr(int reuseToggle)
{
    if ( setsockopt(socketId,SOL_SOCKET,SO_REUSEADDR,(char *)&reuseToggle,sizeof(reuseToggle)) == -1 )
    {
        dasLog << "Error 007 getting host by name" << endl;
        cout << "Error 007 getting host by name" << endl;
        close(socketId);
        exit(1);
    }
}

void GeneralSocket::setKeepAlive(int aliveToggle)
{
    if ( setsockopt(socketId,SOL_SOCKET,SO_KEEPALIVE,(char *)&aliveToggle,sizeof(aliveToggle)) == -1 )
    {
        dasLog << "Error 008 getting host by name" << endl;
        cout << "Error 008 getting host by name" << endl;
        close(socketId);
        exit(1);
    }
}

void GeneralSocket::setLingerSeconds(int seconds)
{
	struct linger lingerOption;
	
	if ( seconds > 0 )
	{
		lingerOption.l_linger = seconds;
		lingerOption.l_onoff = 1;
	}
	else lingerOption.l_onoff = 0;
	 

        if ( setsockopt(socketId,SOL_SOCKET,SO_LINGER,(char *)&lingerOption,sizeof(struct linger)) == -1 )
        {
            dasLog << "Error 009 getting host by name" << endl;
            cout  << "Error 009 getting host by name" << endl;
            close(socketId);
            exit(1);
        }
}

void GeneralSocket::setLingerOnOff(bool lingerOn)
{
	struct linger lingerOption;

	if ( lingerOn ) lingerOption.l_onoff = 1;
	else lingerOption.l_onoff = 0;


        if ( setsockopt(socketId,SOL_SOCKET,SO_LINGER,(char *)&lingerOption,sizeof(struct linger)) == -1 )
        {	
            dasLog << "Error 010 getting host by name" << endl;
            cout << "Error 010 getting host by name" << endl;
            close(socketId);
            exit(1);
        }
	
}

void GeneralSocket::setSendBufSize(int sendBufSize)
{
    if ( setsockopt(socketId,SOL_SOCKET,SO_SNDBUF,(char *)&sendBufSize,sizeof(sendBufSize)) == -1 )
    {
        dasLog << "Error 011 getting host by name" << endl;
        cout << "Error 011 getting host by name" << endl;
        close(socketId);
        exit(1);
    }
} 

void GeneralSocket::setReceiveBufSize(int receiveBufSize)
{
    if ( setsockopt(socketId,SOL_SOCKET,SO_RCVBUF,(char *)&receiveBufSize,sizeof(receiveBufSize)) == -1 )
    {
        dasLog << "Error 012 getting host by name" << endl;
        cout << "Error 012 getting host by name" << endl;
        close(socketId);
        exit(1);
    }
}

void GeneralSocket::setSocketBlocking(int blockingToggle)
{
    if (blockingToggle)
    {
        if (getSocketBlocking()) return;
        else blocking = 1;
    }
    else
    {
            if (!getSocketBlocking()) return;
            else blocking = 0;
    }


    //if windows!
    /*
    if (ioctlsocket(socketId,FIONBIO,(unsigned long *)blocking) == -1)
    {
            dasLog << "error getting host by name" << endl;
            exit(1);
    }
     */

        /*
        //if linux
        if (ioctl(socketId,FIONBIO,(char *)&blocking) == -1)
        {
                myException* unixSocketOptionException = new myException(0,"unix: error getting host by name");
                throw unixSocketOptionException;
        }
         */
	
}

int GeneralSocket::getDebug()
{
    int myOption;
    int myOptionLen = sizeof(myOption);
    
    if ( getsockopt(socketId,SOL_SOCKET,SO_DEBUG,(char *)&myOption,(socklen_t *)&myOptionLen) == -1 )
    {
        dasLog << "Error 013 getting host by name" << endl;
        cout << "Error 013 getting host by name" << endl;
        close(socketId);
        exit(1);
    }
    
    return myOption;
}

int GeneralSocket::getReuseAddr()
{
    int myOption;        
    int myOptionLen = sizeof(myOption);

        if ( getsockopt(socketId,SOL_SOCKET,SO_REUSEADDR,(char *)&myOption,(socklen_t *)&myOptionLen) == -1 )
        {
            dasLog << "Error 014 getting host by name" << endl;
            cout << "Error 014 getting host by name" << endl;
            close(socketId);
            exit(1);
        }

	return myOption;
}

int GeneralSocket::getKeepAlive()
{
    int myOption;        
    int myOptionLen = sizeof(myOption);

    if ( getsockopt(socketId,SOL_SOCKET,SO_KEEPALIVE,(char *)&myOption,(socklen_t *)&myOptionLen) == -1 )
    {
        dasLog << "Error 015 getting host by name" << endl;
        cout << "Error 015 getting host by name" << endl;
        close(socketId);
        exit(1);
    }
                
    return myOption;    
}

int GeneralSocket::getLingerSeconds()
{
	struct linger lingerOption;
	int myOptionLen = sizeof(struct linger);

        if ( getsockopt(socketId,SOL_SOCKET,SO_LINGER,(char *)&lingerOption,(socklen_t *)&myOptionLen) == -1 )
        {
            dasLog << "Error 016 getting host by name" << endl;
            cout << "Error 016 getting host by name" << endl;
            close(socketId);
            exit(1);
        }


	return lingerOption.l_linger;
}

bool GeneralSocket::getLingerOnOff()
{
    struct linger lingerOption;
    int myOptionLen = sizeof(struct linger);


    if ( getsockopt(socketId,SOL_SOCKET,SO_LINGER,(char *)&lingerOption,(socklen_t *)&myOptionLen) == -1 )
    {
        dasLog << "Error 017 getting host by name" << endl;
        cout << "Error 017 getting host by name" << endl;
        close(socketId);
        exit(1);
    }


    if ( lingerOption.l_onoff == 1 ) return true;
    else return false;
}

int GeneralSocket::getSendBufSize()
{
    int sendBuf;
    int myOptionLen = sizeof(sendBuf);


    if ( getsockopt(socketId,SOL_SOCKET,SO_SNDBUF,(char *)&sendBuf,(socklen_t *)&myOptionLen) == -1 )
    {
        dasLog << "Error 018 getting host by name" << endl;
        cout << "Error 018 getting host by name" << endl;
        close(socketId);
        exit(1);
    }

    return sendBuf;
}    

int GeneralSocket::getReceiveBufSize()
{
    int rcvBuf;
    int myOptionLen = sizeof(rcvBuf);
    if ( getsockopt(socketId,SOL_SOCKET,SO_RCVBUF,(char *)&rcvBuf,(socklen_t *)&myOptionLen) == -1 )
    {
        dasLog << "Error 019 getting host by name" << endl;
        cout << "Error 019 getting host by name" << endl;
        close(socketId);
        exit(1);
    }
    return rcvBuf;
}

ostream& operator<<(ostream& io,GeneralSocket& s)
{
    string flagStr = "";

    io << "--------------- Summary of socket settings -------------------" << endl;
    io << "   Socket Id:     " << s.getSocketId() << endl;
    io << "   port #:        " << s.getPortNumber() << endl;
    io << "   debug:         " << (flagStr = s.getDebug()? "true":"false" ) << endl;
    io << "   reuse addr:    " << (flagStr = s.getReuseAddr()? "true":"false" ) << endl;
    io << "   keep alive:    " << (flagStr = s.getKeepAlive()? "true":"false" ) << endl;
    io << "   send buf size: " << s.getSendBufSize() << endl;
    io << "   recv bug size: " << s.getReceiveBufSize() << endl;
    io << "   blocking:      " << (flagStr = s.getSocketBlocking()? "true":"false" ) << endl;
    io << "   linger on:     " << (flagStr = s.getLingerOnOff()? "true":"false" ) << endl;
    io << "   linger seconds: " << s.getLingerSeconds() << endl;
    io << "----------- End of Summary of socket settings ----------------" << endl;
    return io;
}




TcpSocket* TcpSocket::acceptClient(string& clientHost)
{
        int newSocket;   // the new socket file descriptor returned by the accept systme call

    // the length of the client's address
    int clientAddressLen = sizeof(struct sockaddr_in);
    struct sockaddr_in clientAddress;    // Address of the client that sent data

    // Accepts a new client connection and stores its socket file descriptor
    if ((newSocket = accept(socketId, (struct sockaddr *)&clientAddress,(socklen_t*)&clientAddressLen)) < 0)
    {
        dasLog << "Error 020 accapting client!" << endl;
        cout << "Error 020 accapting client!" << endl;
        close(socketId);
        exit(1);
    }
    
    // Get the host name given the address
    char *sAddress = inet_ntoa((struct in_addr)clientAddress.sin_addr);
    HostInfo clientInfo(string(sAddress),ADDRESS);
    char* hostName = clientInfo.getHostName();
    clientHost += string(hostName);
	
    // Create and return the new myTcpSocket object
    TcpSocket* retSocket = new TcpSocket();
    retSocket->setSocketId(newSocket);
    return retSocket;
}

void TcpSocket::bindSocket()
{	
    if (bind(socketId,(struct sockaddr *)&serverAddr,sizeof(struct sockaddr_in))==-1)
    {
        dasLog << "Error 021 binding socket" << endl;
        cout << "Error 021 binding socket" << endl;
        close(socketId);
        exit(1);
    }
}

void TcpSocket::listenToClient(int totalNumPorts)
{
    if (listen(socketId,totalNumPorts) < 0)
    {
        dasLog << "Error 022 listening to client!" << endl;
        cout << "Error 022 listening to client!" << endl;
        close(socketId);
        exit(1);
    }
	
}

bool TcpSocket::connectToServer(const string& serverAddr, hostType hType)
{
    /* 
    when this method is called, a client socket has been built already,
    so we have the socketId and portNumber ready.

    a myHostInfo instance is created, no matter how the server's name is 
    given (such as www.yuchen.net) or the server's address is given (such
    as 169.56.32.35), we can use this myHostInfo instance to get the 
    IP address of the server
    */

    HostInfo serverInfo(serverAddr,hType);
	
    // Store the IP address and socket port number	
    struct sockaddr_in serverAddress;
    serverAddress.sin_family = AF_INET;
    serverAddress.sin_addr.s_addr = inet_addr(serverInfo.getHostIPAddress());
    serverAddress.sin_port = htons(portNumber);

    // Connect to the given address
    if (connect(socketId,(struct sockaddr *)&serverAddress,sizeof(serverAddress)) == -1)
    {

        string errorMsg = "error calling connect():\n";
        return false;
    }
        
    return true;
}

    //soms haalt ie niet alle gevraagde bytes in 1x op, dit fixt dat
int TcpSocket::recieveMessage(string& message)
{
    int numBytes;  // The number of bytes recieved

    // retrieve the length of the message received
    char msgLength[MSG_HEADER_LEN+1];
    memset(msgLength,0,sizeof(msgLength));

    //de 1e 6 bytes haalt ie vrijwel altijd op, als dat niet gebeurt, loopt ie wel de soep in!
    numBytes = recv(socketId,msgLength,MSG_HEADER_LEN,0);
    
    if (numBytes == -1)
    {
        dasLog << "Error 023 calling recv()" << endl;
        cout << "Error 023 calling recv()" << endl;
        close(socketId);
        exit(1);
    }


    //we hebben nu de totale lengte van het aantal bytes wat verzonden is en wat we dus op moeten halen
    int realMsgLength = atoi(msgLength);
    
    //cout << "msgLength(" << numBytes << "): '" << msgLength << "' , realMsgLength: '" << realMsgLength << "' - ";
    
    char msgReal[realMsgLength+1];
    char * msgRealPtr;
    msgRealPtr = msgReal;
    
    //tijdelijke buffer om de resterende bytes in op te slaan als ie alles niet in 1x ophaald
    char msgTMP[realMsgLength+1];
    char * msgTMPPtr;
    msgTMPPtr = msgTMP;
    
    // receive the real message
    //numBytes = recv(socketId,(char*)(message),atoi(msgLength),0);
    
    int posCounter = 0; //houd bij hoeveel bytes van het totaal we al opgehaald hebben
    bool stopLoop = false; //om de loop te stoppen
    int nextAmountOfBytesToReceive = realMsgLength; // het volgende aantal bytes wat we moeten ophalen
    while(!stopLoop){
        numBytes = recv(socketId,msgTMPPtr,nextAmountOfBytesToReceive,0); //haalt shit op
        
        if (numBytes == -1)
        {
            dasLog << "Error 023 calling recv()" << endl;
            cout << "Error 023 calling recv()" << endl;
            close(socketId);
            exit(1);
        }
        
        //zet alles wat we opgehaald hebben in de GOEDE (niet tmp) array op de juiste plek
        int loopCounter = 0;
        for(int i = posCounter; i < (posCounter + numBytes); i++){
            const char tmpCHR = msgTMPPtr[loopCounter];
            msgReal[i] = tmpCHR;
            loopCounter++;
        }
        
        posCounter += numBytes; //voegt het aantal bytes toe wat we opgehaald hebben aan de totaal counter
        nextAmountOfBytesToReceive -= numBytes; //berekent het aantal bytes wat we de volgende keer gaan ophalen
        //als het totaal opgehaalde bytes gelijk of groter (groter komt nooit voor, just to be sure) is dan het eerder vastgestelde aantal, dan moetie er mee kappen!
        if(posCounter >= realMsgLength){
            stopLoop = true;
        }
    }
    numBytes = posCounter;
    //cout << "msgRealPtr(" << numBytes << "): '" << msgRealPtr << "'" << endl;
    if (numBytes == -1)
    {
        dasLog << "Error 024 calling recv()" << endl;
        cout << "Error 024 calling recv()" << endl;
        close(socketId);
        exit(1);
    }
    msgReal[realMsgLength] = 0;
    message = msgRealPtr;
    //cout << "" ;
    //cout << "realMsgLength: " << realMsgLength << ", numBytes: " << numBytes << endl;
    return numBytes;
}
/*
int TcpSocket::recieveMessage(string& message)
{
    int numBytes;  // The number of bytes recieved

    // retrieve the length of the message received
    char msgLength[MSG_HEADER_LEN+1];
    memset(msgLength,0,sizeof(msgLength));

    numBytes = recv(socketId,msgLength,MSG_HEADER_LEN,0);
    cout << "msgLength(" << numBytes << "): '" << msgLength << "' - ";
    if (numBytes == -1)
    {
        dasLog << "Error 023 calling recv()" << endl;
        cout << "Error 023 calling recv()" << endl;
        close(socketId);
        exit(1);
    }


    int realMsgLength = atoi(msgLength);
    char msgReal[realMsgLength+1];
    char * msgRealPtr;
    msgRealPtr = msgReal;
    // receive the real message
    //numBytes = recv(socketId,(char*)(message),atoi(msgLength),0);
    numBytes = recv(socketId,msgRealPtr,realMsgLength,0);
    cout << "msgRealPtr(" << numBytes << "): '" << msgRealPtr << "'" << endl;
    if (numBytes == -1)
    {
        dasLog << "Error 024 calling recv()" << endl;
        cout << "Error 024 calling recv()" << endl;
        close(socketId);
        exit(1);
    }
    msgReal[realMsgLength] = 0;
    message = msgRealPtr;
    //cout << "" ;
    //cout << "realMsgLength: " << realMsgLength << ", numBytes: " << numBytes << endl;
    return numBytes;
}
*/

/*
int TcpSocket::recieveMessage(string& message)
{
	int numBytes;  // The number of bytes recieved
	message = "";

	// retrieve the length of the message received
        char * lenPtr = new char[MSG_HEADER_LEN+1];
	char msgLength[MSG_HEADER_LEN+1];
        
	memset(lenPtr,0,sizeof(lenPtr));
        //memset(msgLength,0,sizeof(msgLength));
	
	
	numBytes = recv(socketId,lenPtr,MSG_HEADER_LEN,0);
        if (numBytes == -1)
        {
            dasLog << "error calling recv()" << endl;
            exit(1);
	}


	// receive the real message
        int dasLen = atoi(lenPtr);
        char * charMessage = new char[dasLen+1];
        numBytes = recv(socketId,charMessage,dasLen,0);
        charMessage[dasLen] = '\0';
        message = charMessage;
        //delete[] charMessage;
        //delete[] lenPtr;
        //numBytes = recv(socketId,(char*)(message.c_str()),atoi(msgLength),0);
        if (numBytes == -1)
        {
            dasLog << "error calling recv()" << endl;
            exit(1);
	}


    return numBytes;
}
*/

int TcpSocket::sendMessage(const string& message)
{
    int numBytes;  // the number of bytes sent

    /* 
       for each message to be sent, add a header which shows how long this message
       is. This header, regardless how long the real message is, will always be
       of the length MSG_HEADER_LEN.
    */

    char msgLength[MSG_HEADER_LEN+1];
    sprintf(msgLength,"%6d",message.size());
    string sendMsg = string(msgLength);
    sendMsg += message;

	// Sends the message to the connected host
    if (numBytes = send(socketId,sendMsg.c_str(),sendMsg.size(),0) == -1)
    {
        dasLog << "Error 025 calling send()!" << endl;
        cout << "Error 025 calling send()!" << endl;
        exit(1);
    }
    
    return numBytes;
}


int TcpSocket::sendFile(const string& fpath)
{

    if(Utility::getInstance()->fileExists(fpath)){
        /*
        this->sendMessage(string("FILE EXISTS"));

        // Extract only filename from given path.
        char filename[50];
        int i=strlen(fpath);
        for(;i>0;i--)if(fpath[i-1]=='\\')break;
        for(int j=0;i<=(int)strlen(fpath);i++)filename[j++]=fpath[i];
        ////////////////////////////////////////

        ifstream myFile (fpath, ios::in|ios::binary|ios::ate);
        int size = (int)myFile.tellg();
        myFile.close();

        char filesize[10];
        Utility::getInstance()->Itoa(size,filesize,10);


        this->sendMessage(string(filename));
        //send( socketId, filename, strlen(filename), 0 );
        char rec[32] = "";
        //recv( socketId, rec, 32, 0 );
        string dasRec;
        this->recieveMessage(dasRec);

        this->sendMessage(string(filesize));
        //send( socketId, filesize, strlen(filesize), 0 );
        //recv( socketId, rec, 32, 0 );
        this->recieveMessage(dasRec);

        if(filesize > 0){
                FILE *fr = fopen(fpath, "rb");

                while(size > 0)
                {
                        char buffer[1030];

                        if(size>=1024)
                        {
                                fread(buffer, 1024, 1, fr);
                                send( socketId, buffer, 1024, 0 );
                                recv( socketId, rec, 32, 0 );

                        }
                        else
                        {
                                fread(buffer, size, 1, fr);
                                buffer[size]='\0';
                                send( socketId, buffer, size, 0 );
                                recv( socketId, rec, 32, 0 );
                        }


                        size -= 1024;

                }


                fclose(fr);
        }
         * */
        return 1;
    } else {
            this->sendMessage(string("FILE NOT EXISTS"));
    }
    return 0; //file not exists
}